#include	"stdafx.h"
#include	"stdio.h"
#include	"math.h"
#include	"Gz.h"
#include	"rend.h"


int GzNewRender(GzRender **render, GzRenderClass renderClass, GzDisplay *display)
{
/* 
- malloc a renderer struct
- keep closed until BeginRender inits are done
- span interpolator needs pointer to display for pixel writes
- check for legal class GZ_Z_BUFFER_RENDER
*/
	(*render) = (GzRender*) operator new (sizeof(GzRender));
	(*render)->renderClass = renderClass;
	(*render)->display = display;
	(*render)->ambientlight = NULL;
	(*render)->camera = NULL;
	(*render)->interp_mode = 0;
	(*render)->matlevel = 0;
	(*render)->numlights = 1;
	(*render)->open = display->open;
	(*render)->spec = DEFAULT_SPEC;
	(*render)->tex_fun = NULL;
	(*render)->Ka[0] = 0.1;			/*throw DEFAULT_AMBIENT, DEFAULT_DIFFUSE, DEFAULT_SPECULAR to Ka, Kd, Ks*/
	(*render)->Ka[1] = 0.1;
	(*render)->Ka[2] = 0.1;
	(*render)->Kd[0] = 0.7;
	(*render)->Kd[1] = 0.6;
	(*render)->Kd[2] = 0.5;
	(*render)->Ks[0] = 0.2;
	(*render)->Ks[1] = 0.3;
	(*render)->Ks[2] = 0.4;
	for(int i=0;i<3;i++)
	{
		(*render)->flatcolor[i] = 0;
	}
	for(int i=0;i<10;i++)
	{
		(*render)->lights[i]=NULL;
	}
	for(int i=0;i<10;i++)
	{
		for(int j=0;j<4;j++)
		{
			for(int k=0;k<4;k++)
			{
				(*render)->Ximage[i][k][j]=0;
				(*render)->Xnorm[i][k][j]=0;
			}
		}
	}
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<4;j++)
		{
			(*render)->Xsp[i][j]=0;
		}
	}

	if(!GZ_Z_BUFFER_RENDER)
	{
		return GZ_FAILURE;
	}
	return GZ_SUCCESS;
}


int GzFreeRender(GzRender *render)
{
/* 
-free all renderer resources
*/
	delete(render);

	return GZ_SUCCESS;
}


int GzBeginRender(GzRender	*render)
{
/* 
- set up for start of each frame - init frame buffer
*/
	GzInitDisplay(render->display);
	return GZ_SUCCESS;
}


int GzPutAttribute(GzRender	*render, int numAttributes, GzToken	*nameList, 
	GzPointer *valueList) /* void** valuelist */
{
/*
- set renderer attribute states (e.g.: GZ_RGB_COLOR default color)
- later set shaders, interpolaters, texture maps, and lights
*/
	for(int i=0;i<numAttributes;i++)
	{
		if(nameList[i]==GZ_RGB_COLOR)//set color
		{
			float* color=(float*)valueList[i];
			render->flatcolor[0]=color[0];
			render->flatcolor[1]=color[1];
			render->flatcolor[2]=color[2];
		}
	}

	return GZ_SUCCESS;
}


int GzPutTriangle(GzRender *render, int	numParts, GzToken *nameList,
	GzPointer *valueList) 
/* numParts - how many names and values */
{
/* 
- pass in a triangle description with tokens and values corresponding to
      GZ_NULL_TOKEN:		do nothing - no values
      GZ_POSITION:		3 vert positions in model space
- Invoke the scan converter and return an error code
*/
	for(int i=0;i<numParts;i++)
	{
		if(nameList[i]==GZ_NULL_TOKEN)
		{
		}
		else if(nameList[i]==GZ_POSITION)//set triangle
		{
			float delta;
			double a,b,c,d;
			double z;
			GzCoord vertex[3];
			float* temp=(float*)valueList[i];
			GzIntensity oldr,oldg,oldb,olda;
			GzDepth oldZ;
			//initial edge DDAs and span DDAs
			EdgeDDA* e1=(EdgeDDA*) operator new (sizeof(EdgeDDA));
			EdgeDDA* e2=(EdgeDDA*) operator new (sizeof(EdgeDDA));
			EdgeDDA* e3=(EdgeDDA*) operator new (sizeof(EdgeDDA));
			SpanDDA* s=(SpanDDA*) operator new (sizeof(SpanDDA));
			//get the location of each point
			vertex[0][0]=temp[0];
			vertex[0][1]=temp[1];
			vertex[0][2]=temp[2];
			vertex[1][0]=temp[3];
			vertex[1][1]=temp[4];
			vertex[1][2]=temp[5];
			vertex[2][0]=temp[6];
			vertex[2][1]=temp[7];
			vertex[2][2]=temp[8];
			//get the color
			short red = ctoi(render->flatcolor[0]);
			short green = ctoi(render->flatcolor[1]);
			short blue = ctoi(render->flatcolor[2]);

			YSort(e1,e2,e3,vertex);//sort verts by Y
			LRTest(e1,e2,e3);//setup edge DDAs and sort them by L or R
			//calculate (a,b,c) of ax+by+cz+d=0
			/*calculate a,b,c for the triangle*/
			a=(int)((((double)vertex[1][1]-(double)vertex[0][1])*((double)vertex[2][2]-(double)vertex[0][2]))-(((double)vertex[1][2]-(double)vertex[0][2])*((double)vertex[2][1]-(double)vertex[0][1])));
			b=(int)((((double)vertex[1][2]-(double)vertex[0][2])*((double)vertex[2][0]-(double)vertex[0][0]))-(((double)vertex[1][0]-(double)vertex[0][0])*((double)vertex[2][2]-(double)vertex[0][2])));
			c=(int)((((double)vertex[1][0]-(double)vertex[0][0])*((double)vertex[2][1]-(double)vertex[0][1]))-(((double)vertex[1][1]-(double)vertex[0][1])*((double)vertex[2][0]-(double)vertex[0][0])));
			/**/

			if((e1->lr!=2)&&(e2->lr!=3))
			{
				AdvanceYCeiling(e1,e3);//Advance and DDA current positions to top y-scan line (ceiling)

				while(e1->vCurrent[1]<=e1->vEnd[1])
				{//start from the lower edge
					InitSpanDDA(s,e1,e3);//Initial span DDA
					d=((a*s->xCurrent+b*e1->vCurrent[1]+c*s->zCurrent)+(a*s->xEnd+b*e1->vCurrent[1]+c*s->zEnd))/2;//calculate d

					delta=(int)s->xStart+1-s->xStart;
					if(delta!=1)
					{
						s->xCurrent=s->xStart+delta;
						s->zCurrent=s->zStart+s->slopez*delta;
					}
					else
					{
						s->xCurrent=s->xStart;
						s->zCurrent=s->zStart;
					}//setup span edges and set positions

					while(s->xCurrent<=s->xEnd)
					{
						z=(d-a*(double)s->xCurrent-b*(double)e1->vCurrent[1])/c;//interpolate z
						s->zCurrent=z;
						GzGetDisplay(render->display,s->xCurrent,e1->vCurrent[1],&oldr,&oldg,&oldb,&olda,&oldZ);//get the z-buffer
						if(z<=oldZ)//check the z with the z-buffer
						{
							GzPutDisplay(render->display,(int)s->xCurrent,(int)e1->vCurrent[1],red,green,blue,1,z);//test interpolate-Z and write color value into FB
						}
						s->xCurrent+=1;//next pixel
						s->zCurrent+=s->slopez;
					}
					//next line
					e1->vCurrent[1]+=1;
					e1->vCurrent[0]=e1->vCurrent[0]+e1->slopex;
					e1->vCurrent[2]=e1->vCurrent[2]+e1->slopez;
					e3->vCurrent[1]+=1;
					e3->vCurrent[0]=e3->vCurrent[0]+e3->slopex;
					e3->vCurrent[2]=e3->vCurrent[2]+e3->slopez;
				}

				delta=(int)e2->vStart[1]+1-e2->vStart[1];
				if(delta!=1)
				{
					e2->vCurrent[0]=e2->vCurrent[0]+e2->slopex*delta;
					e2->vCurrent[1]=e2->vCurrent[1]+delta;
					e2->vCurrent[2]=e2->vCurrent[2]+e2->slopez*delta;
				}//go to higher egde

				while(e3->vCurrent[1]<=e3->vEnd[1])
				{//start the scan line between the higher edge and the othe edge
					InitSpanDDA(s,e2,e3);//Initial span DDA
					d=((a*s->xCurrent+b*e3->vCurrent[1]+c*s->zCurrent)+(a*s->xEnd+b*e3->vCurrent[1]+c*s->zEnd))/2;//calculate d
					delta=(int)s->xStart+1-s->xStart;
					if(delta!=1)
					{
						s->xCurrent=s->xStart+delta;
						s->zCurrent=s->zStart+s->slopez*delta;
					}
					else
					{
						s->xCurrent=s->xStart;
						s->zCurrent=s->zStart;
					}//setup span edges and set positions

					while(s->xCurrent<=s->xEnd)
					{
						z=(d-a*(double)s->xCurrent-b*(double)e2->vCurrent[1])/c;//interpolate z
						s->zCurrent=z;
						GzGetDisplay(render->display,s->xCurrent,e2->vCurrent[1],&oldr,&oldg,&oldb,&olda,&oldZ);//get the z-buffer
						if(z<=oldZ)//check the z with the z-buffer
						{
							GzPutDisplay(render->display,(int)s->xCurrent,(int)e2->vCurrent[1],red,green,blue,1,z);//test interpolate-Z and write color value into FB
						}
						s->xCurrent+=1;//next pixel
						s->zCurrent+=s->slopez;
					}
					//next line
					e2->vCurrent[1]+=1;
					e2->vCurrent[0]=e2->vCurrent[0]+e2->slopex;
					e2->vCurrent[2]=e2->vCurrent[2]+e2->slopez;
					e3->vCurrent[1]+=1;
					e3->vCurrent[0]=e3->vCurrent[0]+e3->slopex;
					e3->vCurrent[2]=e3->vCurrent[2]+e3->slopez;
				}
			}
			else if(e1->lr==2)
			{
				AdvanceYCeiling(e2,e3);//Advance and DDA current positions to top y-scan line (ceiling)
				while(e3->vCurrent[1]<=e3->vEnd[1])
				{//since the top edge is horizontal, only the span edge between other two edges needs to be calculated
					InitSpanDDA(s,e3,e2);//Initial span DDA
					d=((a*s->xCurrent+b*e3->vCurrent[1]+c*s->zCurrent)+(a*s->xEnd+b*e3->vCurrent[1]+c*s->zEnd))/2;//calculate d
					delta=(int)s->xStart+1-s->xStart;
					if(delta!=1)
					{
						s->xCurrent=s->xStart+delta;
						s->zCurrent=s->zStart+s->slopez*delta;
					}
					else
					{
						s->xCurrent=s->xStart;
						s->zCurrent=s->zStart;
					}//setup span edges and set positions

					while(s->xCurrent<=s->xEnd)
					{
						z=(d-a*(double)s->xCurrent-b*(double)e2->vCurrent[1])/c;//interpolate z
						s->zCurrent=z;
						GzGetDisplay(render->display,s->xCurrent,e2->vCurrent[1],&oldr,&oldg,&oldb,&olda,&oldZ);//get the z-buffer
						if(z<=oldZ)//check the z with the z-buffer
						{
							GzPutDisplay(render->display,(int)s->xCurrent,(int)e2->vCurrent[1],red,green,blue,1,z);//test interpolate-Z and write color value into FB
						}
						s->xCurrent+=1;//next pixel
						s->zCurrent+=s->slopez;
					}
					//next line
					e2->vCurrent[1]+=1;
					e2->vCurrent[0]=e2->vCurrent[0]+e2->slopex;
					e2->vCurrent[2]=e2->vCurrent[2]+e2->slopez;
					e3->vCurrent[1]+=1;
					e3->vCurrent[0]=e3->vCurrent[0]+e3->slopex;
					e3->vCurrent[2]=e3->vCurrent[2]+e3->slopez;
				}
			}
			else if(e2->lr==3)
			{
				AdvanceYCeiling(e1,e3);//Advance and DDA current positions to top y-scan line (ceiling)
				while(e1->vCurrent[1]<=e1->vEnd[1])
				{//since the bottom edge is horizontal, only the span edge between other two edges needs to be calculated
					InitSpanDDA(s,e1,e3);//Initial span DDA
					d=((a*s->xCurrent+b*e1->vCurrent[1]+c*s->zCurrent)+(a*s->xEnd+b*e1->vCurrent[1]+c*s->zEnd))/2;//calculate d
					delta=(int)s->xStart+1-s->xStart;
					if(delta!=1)
					{
						s->xCurrent=s->xStart+delta;
						s->zCurrent=s->zStart+s->slopez*delta;
					}
					else
					{
						s->xCurrent=s->xStart;
						s->zCurrent=s->zStart;
					}//setup span edges and set positions

					while(s->xCurrent<=s->xEnd)
					{
						z=(d-a*s->xCurrent-b*e1->vCurrent[1])/c;//interpolate z
						s->zCurrent=z;
						GzGetDisplay(render->display,s->xCurrent,e1->vCurrent[1],&oldr,&oldg,&oldb,&olda,&oldZ);//get the z-buffer
						if(z<=oldZ)//check the z with the z-buffer
						{
							GzPutDisplay(render->display,(int)s->xCurrent,(int)e1->vCurrent[1],red,green,blue,1,z);//test interpolate-Z and write color value into FB
						}
						s->xCurrent+=1;//next pixel
						s->zCurrent+=s->slopez;
					}
					//next line
					e1->vCurrent[1]+=1;
					e1->vCurrent[0]=e1->vCurrent[0]+e1->slopex;
					e1->vCurrent[2]=e1->vCurrent[2]+e1->slopez;
					e3->vCurrent[1]+=1;
					e3->vCurrent[0]=e3->vCurrent[0]+e3->slopex;
					e3->vCurrent[2]=e3->vCurrent[2]+e3->slopez;
				}
			}
		}
	}

	return GZ_SUCCESS;
}

/* NOT part of API - just for general assistance */

short ctoi(float color)		/* convert float color to GzIntensity short */
{
  return(short)((int)(color * ((1 << 12) - 1)));
}

void YSort(EdgeDDA *e1,EdgeDDA *e2,EdgeDDA *e3,GzCoord *vertex)
{//Sort verts by Y from low to high
	GzCoord temp;
	temp[0]=vertex[0][0];
	temp[1]=vertex[0][1];
	temp[2]=vertex[0][2];
	for(int i=0;i<3;i++)
	{
		for(int j=i;j<3;j++)
		{
			if(vertex[j][1]<vertex[i][1])
			{
				temp[0]=vertex[i][0];
				temp[1]=vertex[i][1];
				temp[2]=vertex[i][2];
				vertex[i][0]=vertex[j][0];
				vertex[i][1]=vertex[j][1];
				vertex[i][2]=vertex[j][2];
				vertex[j][0]=temp[0];
				vertex[j][1]=temp[1];
				vertex[j][2]=temp[2];
			}
			else if(vertex[j][1]==vertex[i][1])
			{
				if(vertex[j][0]<=vertex[i][0])
				{
					temp[0]=vertex[i][0];
					temp[1]=vertex[i][1];
					temp[2]=vertex[i][2];
					vertex[i][0]=vertex[j][0];
					vertex[i][1]=vertex[j][1];
					vertex[i][2]=vertex[j][2];
					vertex[j][0]=temp[0];
					vertex[j][1]=temp[1];
					vertex[j][2]=temp[2];
				}
			}
		}
	}
	//Initial edge DDAs
	if(vertex[0][1]==vertex[1][1])
	{//if the top edge is horizontal
		InitEdgeDDAT(e1,vertex[0],vertex[1]);
		InitEdgeDDA(e2,vertex[1],vertex[2]);
		InitEdgeDDA(e3,vertex[0],vertex[2]);

	}
	else if(vertex[1][1]==vertex[2][1])
	{//if the bottom edge is horizontal
		InitEdgeDDAB(e2,vertex[1],vertex[2]);
		InitEdgeDDA(e1,vertex[0],vertex[1]);
		InitEdgeDDA(e3,vertex[0],vertex[2]);
	}
	else
	{
		InitEdgeDDA(e1,vertex[0],vertex[1]);
		InitEdgeDDA(e2,vertex[1],vertex[2]);
		InitEdgeDDA(e3,vertex[0],vertex[2]);
	}
}

void InitEdgeDDA(EdgeDDA *e,float *v1,float *v2)
{//Initial edge DDA
	e->vStart[0]=v1[0];
	e->vStart[1]=v1[1];
	e->vStart[2]=v1[2];
	e->vEnd[0]=v2[0];
	e->vEnd[1]=v2[1];
	e->vEnd[2]=v2[2];
	e->vCurrent[0]=v1[0];
	e->vCurrent[1]=v1[1];
	e->vCurrent[2]=v1[2];
	e->slopex=(v2[0]-v1[0])/(v2[1]-v1[1]);
	e->slopez=(v2[2]-v1[2])/(v2[1]-v1[1]);
	e->lr=L;
}

void InitEdgeDDAT(EdgeDDA *e,float *v1,float *v2)
{//Initial horizontal top edge DDA 
	e->vStart[0]=v1[0];
	e->vStart[1]=v1[1];
	e->vStart[2]=v1[2];
	e->vEnd[0]=v2[0];
	e->vEnd[1]=v2[1];
	e->vEnd[2]=v2[2];
	e->vCurrent[0]=v1[0];
	e->vCurrent[1]=v1[1];
	e->vCurrent[2]=v1[2];
	e->slopex=0;
	e->slopez=0;
	e->lr=T;
}

void InitEdgeDDAB(EdgeDDA *e,float *v1,float *v2)
{//Initial horizontal bottom edge DDA
	e->vStart[0]=v1[0];
	e->vStart[1]=v1[1];
	e->vStart[2]=v1[2];
	e->vEnd[0]=v2[0];
	e->vEnd[1]=v2[1];
	e->vEnd[2]=v2[2];
	e->vCurrent[0]=v1[0];
	e->vCurrent[1]=v1[1];
	e->vCurrent[2]=v1[2];
	e->slopex=0;
	e->slopez=0;
	e->lr=B;
}

void LRTest(EdgeDDA *e1,EdgeDDA *e2,EdgeDDA *e3)
{//Sort edge DDA by L or R
	if(e1->lr==2)
	{//if top edge is horizontal
		e3->lr=L;
		e2->lr=R;
	}
	else if(e2->lr==3)
	{//if bottom edge is horizontal
		e1->lr=L;
		e3->lr=R;
	}
	else
	{
		if(e1->slopex<e3->slopex)
		{
			e3->lr=R;
		}
		else
		{
			e1->lr=R;
			e2->lr=R;
		}//sort L/R by slope
	}
}

void InitSpanDDA(SpanDDA *s,EdgeDDA *e1,EdgeDDA *e2)
{//Initial span DDA
	if(e1->lr==0)
	{//if edge1 is the left edge
		if(e1->vCurrent[0]<0)
		{//if the left end is out of the bound, start the scan line from x=0
			s->xStart=0;
			s->slopez=(e2->vCurrent[2]-e1->vCurrent[2])/(e2->vCurrent[0]-e1->vCurrent[0]);
			s->zStart=e1->vCurrent[2]+s->slopez*(0-e1->vCurrent[0]);
			s->xEnd=e2->vCurrent[0];
			s->zEnd=e2->vCurrent[2];
			s->xCurrent=s->xStart;
			s->zCurrent=s->zStart;
		}
		else
		{
			s->xStart=e1->vCurrent[0];
			s->zStart=e1->vCurrent[2];
			s->xEnd=e2->vCurrent[0];
			s->zEnd=e2->vCurrent[2];
			s->xCurrent=e1->vCurrent[0];
			s->zCurrent=e1->vCurrent[2];
			s->slopez=(s->zEnd-s->zStart)/(s->xEnd-s->xStart);
		}
	}
	else if(e2->lr==0)
	{//if edge2 is left edge
		if(e2->vCurrent[0]<0)
		{//if the left end is out of the bound, start the scan line from x=0
			s->xStart=0;
			s->slopez=(e1->vCurrent[2]-e2->vCurrent[2])/(e1->vCurrent[0]-e2->vCurrent[0]);
			s->zStart=e2->vCurrent[2]+s->slopez*(0-e2->vCurrent[0]);
			s->xEnd=e1->vCurrent[0];
			s->zEnd=e1->vCurrent[2];
			s->xCurrent=s->xStart;
			s->zCurrent=s->zStart;
		}
		else
		{
			s->xStart=e2->vCurrent[0];
			s->zStart=e2->vCurrent[2];
			s->xEnd=e1->vCurrent[0];
			s->zEnd=e1->vCurrent[2];
			s->xCurrent=e2->vCurrent[0];
			s->zCurrent=e2->vCurrent[2];
			s->slopez=(s->zEnd-s->zStart)/(s->xEnd-s->xStart);
		}
	}
}

void AdvanceYCeiling(EdgeDDA *e1,EdgeDDA *e2)
{//Advance and DDA current positions to top y-scan line (ceiling)
	float delta;
	delta=(int)e1->vStart[1]+1-e1->vStart[1];
	if(delta!=1)
	{
		e1->vCurrent[0]=e1->vCurrent[0]+e1->slopex*delta;
		e1->vCurrent[1]=e1->vCurrent[1]+delta;
		e1->vCurrent[2]=e1->vCurrent[2]+e1->slopez*delta;
	}
	delta=(int)e2->vStart[1]+1-e2->vStart[1];
	if(delta!=1)
	{
		e2->vCurrent[0]=e2->vCurrent[0]+e2->slopex*delta;
		e2->vCurrent[1]=e2->vCurrent[1]+delta;
		e2->vCurrent[2]=e2->vCurrent[2]+e2->slopez*delta;
	}
}